Use case: FIR or IIR (ADC input X1, DMA output Y)

This use case shows an FMAC FIR/IIR filter where the X1 input FIFO is fed by the ADC (external peripheral) and the Y output FIFO is read in DMA mode.

Sequence diagram

     @startuml
     title: FIR filter with :\n  - input/X1 managed by the ADC driver \n  - output/Y managed with DMA  process

     participant App as "User application"
     participant App
     participant "<font color=Black><b>HAL FMAC</b></font>" as FMAC
     participant "<font color=green><b>HAL DMA</b></font>" as DMA
     participant "<font color=red><b>HAL ADC</b></font>" as ADC
     participant "FMACx_IRQHandler"
     participant "DMAx_IRQHandler"

     App -> ADC : HAL_ADC_Init
     App <-- ADC : HAL_OK

     App -> FMAC : HAL_FMAC_Init
     App <-- FMAC : HAL_OK

     group ADC System initialization: can be called before ADC HAL initialization
     App -[hidden]>ADC
     end

     group FMAC System initialization: can be called before FMAC HAL initialization
     App -[hidden]>FMAC
     end

     App -> FMAC : HAL_FMAC_FilterConfig()
     note right : The ADC management configuration for input X1 is carried out in this function by setting FMAC_BUFFER_ACCESS_NONE\nin the InputAccess parameter within FMAC_FilterConfigTypeDef structure.\nThe DMA mode configuration for output Y is carried out in this function by setting FMAC_BUFFER_ACCESS_DMA\nin the OutputAccess parameter within FMAC_FilterConfigTypeDef structure.
     App <-- FMAC : HAL_OK

     App -> FMAC : HAL_FMAC_AppendFilterData()
     App <-- FMAC : HAL_OK

     App -> FMAC : HAL_FMAC_FilterStart()
     App <-- FMAC :HAL_OK

     App -> ADC : HAL_ADC_Start_DMA
     note right : Start ADC conversion and store the result in FMAC Write Data register address (@WDATA)
     App <-- ADC : HAL_OK

     "DMAx_IRQHandler" <- : DMAx half complete interrupt
     "DMAx_IRQHandler" -> "DMA" : HAL_DMA_IRQHandler()
     "DMA" -> ADC : XferCpltCallback
     ADC-> App : HAL_ADC_ConvHalfCpltCallback()
     "DMA" <-- ADC
     "DMAx_IRQHandler" <-- "DMA"
     "DMAx_IRQHandler" -->

     "DMAx_IRQHandler" <- : DMAx complete interrupt
     "DMAx_IRQHandler" -> "DMA" : HAL_DMA_IRQHandler()
     "DMA" -> ADC : XferCpltCallback
     ADC-> App : HAL_ADC_ConvCpltCallback()
     "DMA" <-- ADC
     "DMAx_IRQHandler" <-- "DMA"
     "DMAx_IRQHandler" -->

     "DMAx_IRQHandler" <- : DMAx half complete interrupt
     "DMAx_IRQHandler" -> "DMA" : HAL_DMA_IRQHandler()
     "DMA" -> FMAC : XferCpltCallback
     FMAC-> App : HAL_FMAC_HalfOutputDataReadyCallback()
     "DMA" <-- FMAC
     "DMAx_IRQHandler" <-- "DMA"
     "DMAx_IRQHandler" -->

     "DMAx_IRQHandler" <- : DMAx complete interrupt
     "DMAx_IRQHandler" -> "DMA" : HAL_DMA_IRQHandler()
     "DMA" -> FMAC : XferCpltCallback
     FMAC-> App : HAL_FMAC_OutputDataReadyCallback()
     "DMA" <-- FMAC
     "DMAx_IRQHandler" <-- "DMA"
     "DMAx_IRQHandler" -->

     App -> FMAC : HAL_FMAC_FilterStop()
     App <-- FMAC :HAL_OK

     App->FMAC : HAL_FMAC_DeInit()
     App<--FMAC

     @enduml

Code snippet

HAL1

HAL2

ADC_MultiModeTypeDef multimode = {0};
ADC_ChannelConfTypeDef sConfig = {0};

/* FMAC WDATA register address (for DMA transfer) */
#define FMAC_WDATA (0x40021418)

hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_SYNC_PCLK_DIV4;
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.DataAlign = ADC_DATAALIGN_LEFT;
hadc1.Init.GainCompensation = 0;
hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;
hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
hadc1.Init.LowPowerAutoWait = DISABLE;
hadc1.Init.ContinuousConvMode = DISABLE;
hadc1.Init.NbrOfConversion = 1;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIG_HRTIM_TRG1;
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_RISING;
hadc1.Init.DMAContinuousRequests = ENABLE;
hadc1.Init.Overrun = ADC_OVR_DATA_OVERWRITTEN;
hadc1.Init.OversamplingMode = DISABLE;

if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
  Error_Handler();
}

multimode.Mode = ADC_MODE_INDEPENDENT;
if (HAL_ADCEx_MultiModeConfigChannel(&hadc1, &multimode) != HAL_OK)
{
  Error_Handler();
}

sConfig.Channel = ADC_CHANNEL_4;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_2CYCLES_5;
sConfig.SingleDiff = ADC_SINGLE_ENDED;
sConfig.OffsetNumber = ADC_OFFSET_1;
sConfig.Offset = REF;
sConfig.OffsetSign = ADC_OFFSET_SIGN_NEGATIVE;
sConfig.OffsetSaturation = DISABLE;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
  Error_Handler();
}

HAL_FMAC_Init(&hfmac);

sFmacConfig.InputBaseAddress  = INPUT_BUFFER_BASE;
sFmacConfig.InputBufferSize   = INPUT_BUFFER_SIZE;
sFmacConfig.InputThreshold    = INPUT_THRESHOLD;
sFmacConfig.CoeffBaseAddress  = COEFFICIENT_BUFFER_BASE;
sFmacConfig.CoeffBufferSize   = COEFFICIENT_BUFFER_SIZE;
sFmacConfig.OutputBaseAddress = OUTPUT_BUFFER_BASE;
sFmacConfig.OutputBufferSize  = OUTPUT_BUFFER_SIZE;
sFmacConfig.OutputThreshold   = OUTPUT_THRESHOLD;
sFmacConfig.pCoeffA           = aFilterCoeffA;
sFmacConfig.CoeffASize        = COEFF_VECTOR_A_SIZE;
sFmacConfig.pCoeffB           = aFilterCoeffB;
sFmacConfig.CoeffBSize        = COEFF_VECTOR_B_SIZE;
sFmacConfig.Filter            = FMAC_FUNC_IIR_DIRECT_FORM_1;
sFmacConfig.InputAccess       = FMAC_BUFFER_ACCESS_NONE;
sFmacConfig.OutputAccess      = FMAC_BUFFER_ACCESS_DMA;
sFmacConfig.Clip              = FMAC_CLIP_DISABLED;
sFmacConfig.P                 = COEFF_VECTOR_B_SIZE;
sFmacConfig.Q                 = COEFF_VECTOR_A_SIZE;
sFmacConfig.R                 = GAIN;

HAL_FMAC_FilterConfig(&hfmac, &sFmacConfig);

HAL_FMAC_FilterStart(&hfmac, Output, OutputDataSize);

/* Assign FMAC Write Data register (@WDATA) to Fmac_Wdata */
Fmac_Wdata = (uint32_t *) FMAC_WDATA;

/* Start ADC and DMA */
HAL_ADC_Start_DMA(&hadc1, Fmac_Wdata, OUTPUT_BUFFER_SIZE*4);

HAL_FMAC_FilterStop(&hfmac);

HAL_FMAC_DeInit(&gh_fmac);
hal_adc_config_t adc_config;

HAL_ADC_Init(&hADC1, HAL_ADC1);

adc_config.resolution          = HAL_ADC_RESOLUTION_12_BIT;
adc_config.sampling_mode       = HAL_ADC_SAMPLING_MODE_NORMAL;
HAL_ADC_SetConfig(&hADC1, &adc_config);

hal_adc_reg_config_t reg_config;
reg_config.trigger_src        = HAL_ADC_REG_TRIG_SOFTWARE_START;
reg_config.trigger_edge       = HAL_ADC_REG_TRIG_EDGE_NONE;
reg_config.sequencer_length   = 1;
reg_config.sequencer_discont  = HAL_ADC_REG_SEQ_DISCONT_DISABLE;
reg_config.continuous         = HAL_ADC_REG_CONV_SINGLE;
reg_config.overrun            = HAL_ADC_REG_OVR_DATA_OVERWRITTEN;
reg_config.data_transfer      = HAL_ADC_REG_DATA_TRANSFER_NONE;
HAL_ADC_REG_SetConfig(&hADC1, &reg_config);

hal_adc_channel_config_t adc_channel_config;
adc_channel_config.group           = HAL_ADC_GROUP_REGULAR;
adc_channel_config.sequencer_rank  = 1;
adc_channel_config.input_mode      = HAL_ADC_IN_SINGLE_ENDED;
adc_channel_config.sampling_time   = HAL_ADC_SAMPLING_TIME_2CYCLES_5;
HAL_ADC_SetConfigChannel(&hADC1, HAL_ADC_CHANNEL_4, &adc_channel_config);

HAL_FMAC_Init(&hfmac, (hal_fmac_t)FMAC);

sFmacConfig.x1_buf.base_half_word = INPUT_BUFFER_BASE;
sFmacConfig.x1_buf.size_half_word = INPUT_BUFFER_SIZE;
sFmacConfig.x2_buf.base_half_word = COEFFICIENT_BUFFER_BASE;
sFmacConfig.x2_buf.size_half_word = COEFFICIENT_BUFFER_SIZE;
sFmacConfig.y_buf.base_half_word  = COEFFICIENT_BUFFER_SIZE;
sFmacConfig.y_buf.size_half_word  = OUTPUT_BUFFER_BASE;

HAL_FMAC_SetConfig(&hfmac, &sFmacConfig);

data.coeff_a_size_half_word = COEFF_VECTOR_A_SIZE;
data.p_coeff_a              = aFilterCoeffA;
data.coeff_b_size_half_word = COEFF_VECTOR_B_SIZE;
data.p_coeff_b              = aFilterCoeffB;

HAL_FMAC_PreloadX2(&hfmac, &data);

/* Obtain the FMAC Write Data register (@WDATA) for ADC */
Fmac_Wdata = HAL_FMAC_GetX1Address(&hfmac);
HAL_FMAC_LockX1WriteAccessForExternPeriph(&hfmac);

HAL_FMAC_ReadY_DMA(&hfmac, Output, OutputDataSize);
HAL_FMAC_StartFilterIIR(&hfmac, COEFF_VECTOR_B_SIZE, COEFF_VECTOR_A_SIZE, GAIN, HAL_FMAC_OPT_IT_FILTER_SATURATION_ERROR);

/* Start ADC and DMA toward FMAC X1 */
HAL_ADC_Start(hadc1);
HAL_ADC_REG_StartConv_DMA(hadc1, (uint8_t const *)Fmac_Wdata, OUTPUT_BUFFER_SIZE*4);

HAL_FMAC_StopFilter(&hfmac);
HAL_FMAC_UnlockX1WriteAccessForExternPeriph(&hfmac);
HAL_FMAC_DeInit(&gh_fmac);

Key changes

  1. HAL2 supports external-peripheral feeding of X1 by providing helpers such as HAL_FMAC_GetX1Address() and explicit lock/unlock of X1 write access for external peripherals.

  2. HAL2 keeps the Y DMA read flow explicit through HAL_FMAC_ReadY_DMA() and completion callbacks.